home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / BCD.PART1.ARC / BCD PART 1
Encoding:
Text File  |  2019-04-13  |  31.1 KB  |  634 lines

  1. *********************************************************************
  2. This article is being presented through the *StarBoard* Journal of
  3. the FlagShip/StarShip, SIGS (Special Interest Groups) on the
  4. Delphi and GEnie telecommunications networks.  Permission is
  5. hereby granted to non-profit organizations only to reprint this
  6. article or pass it along electronically as long as proper credit
  7. is given to both the author and the *StarBoard* Journal.
  8. *********************************************************************
  9.  
  10. THE ABC's OF BCD (part 1)
  11.  
  12. Manipulating Decimal Numbers In Machine Language
  13.  
  14. by W.J. Brier
  15. (TROUBLESOME on DELPHI)
  16.  
  17. ===========================================================================
  18. INTRODUCTION
  19.  
  20. Many tasks that are routinely performed in Commodore (CBM) BASIC can often
  21. be performed more efficiently in machine language.  Tasks such as searching
  22. and sorting are performed in machine language at speeds many times that of
  23. BASIC and usually with less program overhead.  Curiously, although many
  24. search, sort and file handling routines are coded in machine language, few
  25. decimal arithmetic and number handling machine language programs seem to be
  26. written.  Perhaps the concept of doing math in machine language is too
  27. intimidating for most folks.  Or maybe it's because the usual internal
  28. format of numbers is in binary notation.  Whatever the cause of this "fear"
  29. it is actually unfounded.  The MOS 65XX/85XX microprocessor (CPU) in your
  30. Commodore computer is equipped with instructions for doing elementary
  31. mathematical operations in decimal format.  While performing math in
  32. machine language is not as simple as it is in BASIC it certainly is not
  33. beyond the capability of a reasonably proficient machine language
  34. programmer.  This series of articles will attempt to remove some of the
  35. mystery surrounding decimal mathematics in machine language and will
  36. present programming examples illustrating machine language math techniques.
  37.  
  38. Before we begin this discussion I would like to point out that this article
  39. series is not intended to teach elementary machine language programming
  40. techniques.  Program examples presented herein are written with the
  41. assumption that the reader has some degree of familiarity with 65XX/85XX
  42. machine language programming.  If you are not familiar with the 65XX/85XX
  43. instruction set, I recommend that you study one or more of several texts on
  44. the subject and follow the examples presented therein.  If you are familiar
  45. with machine language techniques then the subject matter in this article
  46. will be understandable to you.  Throughout this text the terms "ASCII
  47. decimal" and "decimal" may be interchangeably used, except in cases where
  48. confusion may occur.  In either case, the term refers to the display form
  49. of the number being operated upon, which would be represented by a string
  50. of PETASCII characters.
  51.  
  52. Credit for the source of some of the material to be presented herein must
  53. be extended to Lance A. Leventhal, whose excellent book 6502 ASSEMBLY
  54. LANGUAGE PROGRAMMING (Osborne/McGraw Hill) formed the basis of this
  55. article.  Additional reference was made to PROGRAMMING THE 6502 by Rodney
  56. Zaks, in which a presentation of the concept of BCD notation is made.
  57.  
  58. I.  NUMBERING SYSTEMS
  59.  
  60. Almost from the day that you first went to school you were (and still are)
  61. surrounded by the decimal number system.  No doubt the decimal system
  62. developed because human beings have ten fingers with which to count.  If we
  63. had been equipped with 16 fingers we'd probably be counting in hexadecimal
  64. (base 16).  Decimal arithmetic is so ingrained in us that we routinely do
  65. calculations in decimal without ever actually pausing to consider just what
  66. it is we are doing.
  67.  
  68. The designers of your computer were of course aware of this fact and
  69. programmed the BASIC interpreter so that it would handle ASCII decimal
  70. numbers.  However, curious things occasionally happen in BASIC decimal
  71. arithmetic.  You subtract two large numbers that are approximately equal to
  72. each other and the machine gives you an unexpected result.  The result is
  73. very close to what it should be but is off by a tiny amount.
  74.  
  75. This phenomenon occurs because the machine does not internally represent
  76. ASCII decimal numbers in decimal format.  CBM BASIC stores numbers in one
  77. of two formats...normalized floating point binary, or two byte signed
  78. binary integers, neither in any way directly related to ASCII decimal
  79. notation.  Decimal to binary conversion is performed because the most
  80. elementary functions within the machine are handled by simple YES/NO or
  81. ON/OFF operations, operations best performed in binary.
  82.  
  83. When you ask BASIC to print the value of variable A for example, the value
  84. is converted from normalized floating point binary to ASCII decimal for
  85. display purposes only.  Unfortunately, not all numbers will evenly convert
  86. from binary to decimal or vice versa.  As a result, small but detectable
  87. errors will creep in and may result in display errors.  This is not any
  88. fault of the machine or of BASIC but is a natural result of converting from
  89. one number base to another.
  90.  
  91. The form of a normalized floating point binary number is not particularly
  92. easy to explain or understand.  Basically, such a number consists of an
  93. implied mantissa with a value of one and a five byte exponent which
  94. represents some power of two.  The sign of the number is carried along with
  95. this exponent.  When the BASIC interpreter is called upon to display such a
  96. number it calls the FOUT subroutine located in the interpreter to decode
  97. and display the number.  If a ASCII decimal number is assigned to a
  98. variable then the FIN routine is used to convert the number from decimal to
  99. floating point binary.  It is these conversion operations that introduce
  100. errors.
  101.  
  102. In many programs such conversion errors are of little consequence and are
  103. often ignored.  However, some applications such as financial programs
  104. demand that the decimal amounts be exact, not approximate.  In BASIC of
  105. course the programmer has to make do with the routines built into the
  106. interpreter.  Although the same routines may be called in machine language
  107. there is really no good reason to do so (except for laziness on the part of
  108. the programmer) as the CPU has instructions for performing elementary
  109. decimal math.  By using these instructions in an intelligent manner it is
  110. possible to handle the majority of the math problems that the average
  111. program might encounter.  Additional use of logical operations in the CPU
  112. instruction set may be used to encode and decode numbers, just as the
  113. interpreter encodes and decodes numbers.
  114.  
  115. In this article you will see how to build an internal numbering system that
  116. is based on decimal notation...one that will not introduce conversion
  117. errors when changing from one format to another.  This system will be in
  118. what is referred to as BINARY CODED DECIMAL (BCD) notation.  Unlike
  119. floating point binary there will be a direct relationship between a BCD
  120. number and its ASCII decimal equivalent.
  121.  
  122. II.  WHAT IS A BCD NUMBER?
  123.  
  124. A BCD number is a ASCII decimal number that is represented by one or more
  125. BCD digits.  Any BCD digit may have an unsigned ASCII decimal value of from
  126. 0 to 99.  The range of a BCD number depends on the quantity of BCD digits
  127. used to represent that number (a sign can also be represented when signed
  128. numbers are required).  A curious but nevertheless useful characteristic of
  129. a BCD digit is that in hexadecimal notation it looks just like the ASCII
  130. decimal number that it represents.
  131.  
  132. A single BCD digit is evaluated by splitting it into a high and low nybble,
  133. the high nybble equivalent to the "tens" and the low nybble equivalent to
  134. the "units" of the ASCII decimal value represented by the BCD digit.
  135. Because two ASCII decimal digits are represented by a single BCD digit the
  136. format is said to be "packed" or "compressed".
  137.  
  138. Let's look at some packed BCD digits and see the relationship between ASCII
  139. decimal, and the BCD decimal, hexadecimal and binary values:
  140.  
  141.     ASCII             DEC          HEX         BINARY
  142.     ===================================================
  143.      0                  0          $00       %0000 0000
  144.      1                  1          $01       %0000 0001
  145.      5                  5          $05       %0000 0101
  146.      9                  9          $09       %0000 1001
  147.     10                 16          $10       %0001 0000
  148.     16                 22          $16       %0001 0110
  149.     19                 25          $19       %0001 1001
  150.     20                 32          $20       %0010 0000
  151.     32                 50          $32       %0011 0010
  152.     50                 80          $50       %0101 0000
  153.     75                117          $75       %0111 0101
  154.     99                153          $99       %1001 1001
  155.     ===================================================
  156.  
  157. The ASCII value is the value that you would actually display or input in
  158. your program.  The other values are what the ASCII value would convert to
  159. in BCD format.  Note that the hex equivalent appears to look just like the
  160. ASCII decimal value and that the "straight" decimal value seems to have
  161. little resemblance to the ASCII equivalent.
  162.  
  163. One of the advantages of the BCD format is that certain types of numbers
  164. can be stored in less space than they would otherwise require in ASCII
  165. decimal format.  For example, the time-of-day in ASCII decimal would
  166. require six bytes of storage (two for the hours, two for the minutes and
  167. two for the seconds).  In BCD only three bytes would be required.  A date
  168. such as 11-18-85, which would require eight bytes of storage, could be
  169. reduced to the BCD equivalent of:
  170.  
  171.     $11  $18  $85
  172.  
  173. which can be stored in only three bytes.  However in many cases, a BCD
  174. number will consume more memory than a normalized floating binary
  175. equivalent would.  In modern computers this is not a liability as copious
  176. amounts of memory are available for storage.
  177.  
  178. The main advantage in using BCD notation is the fact that conversions
  179. between ASCII decimal and BCD are exact.  There is no likelihood of a
  180. format conversion error as there would be in binary to decimal or vice
  181. versa.  Also, conversions can performed much faster than floating binary to
  182. decimal conversions.  No look-up tables are needed to transform a BCD digit
  183. to its ASCII decimal equivalent.  The result is a neater and less
  184. cumbersome program.
  185.  
  186. The next chapters will introduce the methods by which BCD numbers are
  187. organized and stored in RAM and converted from one format to the other.
  188.  
  189. III.  ORGANIZING AND STORING BCD NUMBERS
  190.  
  191. When a ASCII decimal number is assigned from CBM BASIC to a variable, the
  192. interpreter converts and stores it as a five byte binary number.
  193. Regardless of the size of the ASCII decimal number that has been assigned
  194. to the variable it will always end up as five bytes.  There are good
  195. reasons for this.  It is much easier to perform mathematical operations if
  196. all of the numbers are of the same length and form.  Also, when converting
  197. from one format to the other, a consistent length makes conversion less
  198. difficult.
  199.  
  200. As with floating point binary notation, BCD notation is best fixed to a
  201. particular length.  You, as the programmer, have the responsibility of
  202. determining just how long your BCD numbers are to be.  The length is
  203. primarily governed by the desired place accuracy of the number (as is also
  204. the case with floating point binary).  Therefore one of the first decisions
  205. to be made in coding the program is to determine the maximum size of the
  206. BCD numbers that are to be manipulated and then assign adequate storage
  207. space to accommodate them.
  208.  
  209. Let's suppose that you are writing a financial program in which you will
  210. add and subtract dollar amounts.  Let's further suppose that the maximum
  211. dollar amount that will have to be accommodated will be 9999.99 in ASCII
  212. decimal.  In BCD this value would be notated as:
  213.  
  214.     $99  $99  $99
  215.  
  216. Part of your program's function will be to "know" where the decimal point
  217. is actually located (between the second and third BCD digits in the above
  218. example).  For computational purposes the location of the decimal point
  219. will be unimportant.  In converting from one format to the other however,
  220. you must know where that decimal point is to be placed.
  221.  
  222. To store an ASCII decimal number in BCD format a byte of storage is
  223. required for every two ASCII digits.  If you were to provide four bytes of
  224. storage you could then store a ASCII decimal equivalent of 999,999.99:
  225.  
  226.     $99  $99  $99  $99
  227.  
  228. So, you can see that there is a direct relationship between the desired
  229. place accuracy in ASCII decimal and the number of bytes required for the
  230. equivalent BCD storage.  Also, a fundamental feature of BCD numbers is that
  231. they always represent even amounts of ASCII decimal digits, as can be seen
  232. from the above examples.  If you want to limit the maximum ASCII decimal
  233. range to 99,999.99 you still have to supply four bytes of storage and
  234. simply ignore the high nybble of the first byte.
  235.  
  236. Our value would then be:
  237.  
  238.     $09  $99  $99  $99
  239.  
  240. In this case the high nybble of the first byte is not actually used to
  241. represent a ASCII decimal digit and you can make alternate use of it to
  242. store a sign bit for the number.  Let's suppose that the ASCII decimal
  243. value is now -99,999.99.  You can represent that in BCD as:
  244.  
  245.     $89  $99  $99  $99
  246.  
  247. Hey, you say, where did that $89 come from?  It is there because the
  248. seventh bit of that digit has been set to one, indicating that the BCD
  249. number is negative.  Let's break that digit down into its binary equivalent
  250. for a closer look:
  251.  
  252.     $89 = %1000 1001
  253.  
  254. Note that the seventh bit of the number has been set to 1, thus giving the
  255. high nybble the value of $8.  The low nybble has the value of $9.  So, if
  256. you patch a high nybble of $8 to a low nybble of $9 you get a byte that is
  257. $89.  Incidentally, the use of the % sign indicates a binary notated
  258. number, just as the $ sign indicates a number notated in hexadecimal.
  259.  
  260. There is an excellent reason for using the seventh bit to indicate the sign
  261. of the number.  If you load a CPU register with that digit and the sign bit
  262. is set, the N flag in the CPU status register will be set and you will
  263. immediately know that the number is negative.  If the sign bit is cleared
  264. then the N flag will likewise clear.  Using the BIT instruction on the
  265. first digit will also set or clear the N flag.  As you will soon see, this
  266. feature will become quite useful when it comes time to actually evaluate
  267. BCD numbers for mathematical purposes.
  268.  
  269. Regardless of the length of your BCD numbers the seventh bit of the first
  270. BCD digit will always act as the sign flag.  If you have allotted four
  271. bytes in which to store the BCD number, you have set a ASCII decimal range
  272. of from -99,999.99 (assuming that you are handling dollars and cents
  273. values) to + 799,999.99, this range in BCD being represented as:
  274.  
  275.     -99,999.99 = $89  $99  $99  $99
  276.     799,999.99 = $79  $99  $99  $99
  277.  
  278. In binary the value of 799,999.99 would be:
  279.  
  280.     %0111 1001  %1001 1001  %1001 1001  %1001 1001
  281.  
  282. It is now apparent why the positive value of the number cannot exceed
  283. 799,999.99.  The next value (800,000.00) would set the seventh bit of the
  284. first digit and the number would now be negative, resulting in an erroneous
  285. value.
  286.  
  287. If your program requires a greater range simply allocate more storage for
  288. the BCD number.  With the available RAM in the C-64 or C-128 gigantic
  289. numbers can be stored without resulting in any serious drain on the
  290. resources of the machine.  For example, the number 10^100 can be stored in
  291. 50 bytes of RAM.  If you were to display the ASCII decimal equivalent of
  292. this number on the C-64 it would use up almost three screen lines.  Here is
  293. the number in ASCII decimal notation:
  294.  
  295.     100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
  296.     000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
  297.     000,000
  298.  
  299. Clearly, BCD notation permits you to store and represent numbers far
  300. greater than CBM BASIC would ever allow.
  301.  
  302. When assigning memory storage for BCD numbers you should exercise some
  303. restraint in estimating the maximum size of the numbers to be stored.
  304. There is little point in assigning 20 bytes of RAM to store BCD numbers for
  305. use in a personal checking account program, as few individuals maintain a
  306. balance of 40 figures.  The more BCD digits assigned to store a number the
  307. longer it will take to perform mathematical or conversion operations, as
  308. well as transfer numbers from one location in memory to another.
  309.  
  310. Another factor to consider is the desired place accuracy to the right of
  311. the decimal point.  Strictly speaking, in BCD notation the decimal point
  312. doesn't actually exist anywhere.  The decimal point gets into the picture
  313. only when converting between ASCII decimal and BCD.  However, when
  314. assigning memory for BCD storage, you must allow room for digits to the
  315. right of the decimal point in order to secure adequate place accuracy.  For
  316. example, to store a range of numbers from +79,999,999.999999 to
  317. -9,999,999.999999 you would need seven bytes of storage, which would result
  318. in the following BCD numbers:
  319.  
  320.     $79  $99  $99  $99  $99     $99  $99  $99
  321.     $89  $99  $99  $99  $99     $99  $99  $99
  322.  
  323. Assigning this much storage results in resolution to a value of .000001 for
  324. each number to be stored.  The imaginary decimal point would be located
  325. between the fifth and sixth BCD digits in each number (as shown by the
  326. extra space between the digits).  Also note that the sign bit has been set
  327. on the second number, indicating that it is negative.  Such storage must be
  328. assigned for each BCD number to be stored in your program.  For convenience
  329. in simultaneously initializing (zeroing out) all values, the number storage
  330. areas should be in contiguous memory, as assigned by your assembler.
  331.  
  332. In addition to storage for each of the BCD numbers to be used in your
  333. program you will need a work area for performing mathematical and
  334. conversion operations.  This work area will consist of two BCD
  335. accumulators, an ASCII accumulator and two bytes of storage for sign flags
  336. for each BCD accumulator.  Additional work space will be required if
  337. multiplication or division operations are to be performed.  This additional
  338. area will be used for storing partial products or quotients.  As with the
  339. actual storage for the BCD numbers, the accumulators, sign flags and
  340. partial product/quotient storage areas should be in contiguous RAM.
  341.  
  342. Both BCD accumulators must be equal in size to each other and as large or
  343. larger than the size of the largest BCD number to be manipulated.
  344. Individual numbers may be smaller than the accumulators without incurring
  345. any problems in performing math operations.  However, varying sizes of BCD
  346. numbers may make ASCII-BCD conversion more difficult to perform.  It is
  347. recommended that all BCD numbers be stored in equal sized areas with the
  348. digit placement the same in all cases.
  349.  
  350. The ASCII accumulator is used to convert between the BCD and ASCII formats.
  351. Its length must be equal to the largest ASCII string of digits to be
  352. displayed plus an additional three bytes of storage.  These extra three
  353. bytes will containing the decimal point, the sign (if negative) and a zero
  354. byte terminator.  For example, the ASCII decimal number -1234.56 would be
  355. stored in the ASCII accumulator as follows:
  356.  
  357.     HEX       2D  31  32  33  32  2E  35  36  00
  358.  
  359.     ASCII      -   1   2   3   4   .   5   6
  360.  
  361. The zero byte at the end of the ASCII string acts as a terminator and
  362. facilitates transferring or displaying the number.  It indicates the end of
  363. the ASCII string (a zero byte in Commodore computers has no significance
  364. other than setting the Z flag in the CPU when loading the zero into a
  365. register).  This feature will be quite useful in moving ASCII strings from
  366. one place to another in memory.
  367.  
  368. With the matter of storage considered, attention will now be focused on
  369. conversion between ASCII and BCD format.  The following symbols will be
  370. used during these discussions:
  371.  
  372.     SYMBOL        REPRESENTS            LENGTH
  373.     ==========================================
  374.     ACUM1         BCD Accumulator #1       4
  375.     ACUM2         BCD Accumulator #2       4
  376.     ACUMA         ASCII Accumulator       10
  377.     SFLG1         ACUM1 Sign flag          1
  378.     SFLG2         ACUM2 Sign flag          1
  379.     .A            CPU Accumulator         --
  380.     .X            CPU X Register          --
  381.     .Y            CPU Y Register          --
  382.     ==========================================
  383. In addition to their primary duties of storing the sign of the number in
  384. their respective accumulators the two sign flags will also be used for
  385. temporary storage when converting between ASCII and BCD.  For the purpose
  386. of discussion it will be assumed that each BCD accumulator is four bytes in
  387. length, as are all BCD numbers referenced in the program examples.  The
  388. ASCII accumulator will be an assumed length of ten bytes, including the
  389. terminator byte ($00).  Means by which the ASCII value in ACUMA is
  390. transferred to or fetched from memory will be discussed in another chapter.
  391.  
  392. IV.  CONVERTING TWO ASCII DIGITS TO A BCD DIGIT
  393.  
  394. As mentioned before there is a "two for one" relationship between ASCII
  395. numbers and their BCD equivalents.  Therefore, conversion from ASCII to BCD
  396. is predicated on the concept of transferring two ASCII digits into a single
  397. BCD digit.  This implies of course that all ASCII numbers in a BCD-based
  398. system contain an even number of digits.  In those cases where the ASCII
  399. number is represented by an odd number of digits means must be developed to
  400. substitute an ASCII zero ($30) for the "missing" digit.
  401.  
  402. For our initial example we will assume that the conversion will be per-
  403. formed on two ASCII digits:
  404.  
  405.     57
  406.  
  407. If our conversion is correct we should be left with the single BCD digit:
  408.     $57
  409.  
  410. Conversion of two ASCII digits to a BCD digit is accomplished in logical
  411. steps:
  412.  
  413.     1.  Load the .A register with the lower of the two ASCII digits
  414.     (the "units") and load the .X register with the higher digit (the
  415.     "tens");
  416.  
  417.     2.  Change the byte in the .A register from an ASCII value to a
  418.     binary value and store it in RAM;
  419.  
  420.     3.  Transfer the byte in the .X register to the .A register and
  421.     change it from ASCII to binary;
  422.  
  423.     4.  Left shift the binary value to the high nybble position in the
  424.     .A register;
  425.  
  426.     5.  Logically OR the .A register with the units nybble that was
  427.     stored in RAM.  The BCD digit is now in the .A register.
  428.  
  429. For ease of use within your program steps 2 through 6 should be structured
  430. into a subroutine, which we will refer to with the symbol ASBCD in this
  431. discussion.
  432. Then, whenever you need to convert two ASCII digits to BCD, simply use the
  433. following code:
  434.  
  435.              LDA UNITS      ;ASCII "UNITS"
  436.              LDX TENS       ;ASCII "TENS"
  437.              JSR ASBCD      ;CALL CONVERSION ROUTINE
  438.              STA ------     ;STORE BCD DIGIT IN RAM
  439.  
  440. The subroutine ASBCD would be structured as follows:
  441.  
  442.     ASBCD    SEC
  443.              SBC #$30       ;ASCII TO BINARY
  444.              STA SFLG2      ;STORE IN RAM
  445.                             ;
  446.              SEC
  447.              TXA            ;MOVE TENS TO .A REGISTER
  448.              SBC #$30       ;ASCII TO BINARY
  449.                             ;
  450.              ASL A          ;LEFT-SHIFT LOW NYBBLE...
  451.              ASL A          ;TO HIGH NYBBLE POSITION
  452.              ASL A
  453.              ASL A
  454.                             ;
  455.              ORA SFLG2      ;COMBINE WITH LOW NYBBLE
  456.                             ;
  457.              RTS            ;BCD DIGIT IS IN .A REGISTER
  458.  
  459. Upon exiting this subroutine the .A register will contain the BCD digit,
  460. the .X register will still contain the ASCII "tens" digit and the .Y
  461. register will be unchanged.  The subroutine as presented makes no attempt
  462. to filter out non-numeric ASCII characters (hex values outside of the range
  463. of $30 through $39).  This should be performed by the portion of your
  464. program that fetches the ASCII number.  Let's see exactly what this
  465. subroutine does to the values presented to it.  The ASCII decimal number 57
  466. is to be encoded into BCD.
  467.  
  468. The first step is to change the ASCII units value ($37) to binary.  This is
  469. simply accomplished by subtracting $30 from the .A register, the result
  470. ($07) being temporarily stored in RAM.  Next, the tens value ($35) is moved
  471. to the .A register and $30 is subtracted from it resulting in a value of
  472. $05.  The four ASL operations shift the $05 to the left until the .A
  473. register finally contains $50.  Each shift would look like this in binary:
  474.     .A Register     0000 0101
  475.     1st shift       0000 1010
  476.     2nd shift       0001 0100
  477.     3rd shift       0010 1000
  478.     4th shift       0101 0000
  479.  
  480. The result is that the value formerly contained in the low nybble is now in
  481. the high nybble and the low nybble is set to zero.
  482.  
  483. The final step is to logically OR the .A register with the low nybble saved
  484. at SFLG2, resulting in the value $57 being left in the .A register.  Here
  485. is what the process would look like in binary:
  486.  
  487.     .A Register     0101 0000 = $50
  488.     ORA SFLG2       0000 0111 = $07
  489.     ===============================
  490.     .A Register     0101 0111 = $57
  491.  
  492. The ASCII decimal number 57 has been converted to the BCD digit $57 and is
  493. in the .A register awaiting storage.
  494.  
  495. It is apparent from studying this routine that the ASCII number to be
  496. converted must be two digits in length.  If it is a single digit number
  497. then the .X register must be loaded with $00.  For example, the ASCII
  498. number 7 would convert to $07 in this routine.  To avoid an odd number of
  499. digits in the ASCII number the ASCII string should be left and/or right
  500. padded with zeros to achieve an even number of digits.  For the sake of
  501. easy conversion the padding should be such that all ASCII numbers are
  502. identical in length.  Means to accomplish this will be discussed later on.
  503.  
  504. V.  CONVERTING A BCD DIGIT TO TWO ASCII DIGITS
  505.  
  506. The previous chapter presented the method of encoding two ASCII digits into
  507. a single BCD digit.  The reverse operation, decoding a BCD digit into two
  508. ASCII digits, will now be discussed.
  509.  
  510. Because the BCD digit is only a single value, logical operations are
  511. required to separate the high and low nybbles so that they can be
  512. individually extracted and changed from binary to ASCII, just as logical
  513. operations were required to originally create the digit.  Thus the
  514. operation is the reverse of the encoding routine presented in ASBCD.  The
  515. decoding routine will be called (logically enough) BCDAS.
  516.  
  517. Essentially, the procedure is as follows:
  518.  
  519.     1.  Load the .A register with the BCD digit to be decoded;
  520.  
  521.     2.  Save the BCD value on the stack;
  522.  
  523.     3.  Extract the low nybble from the BCD value, convert it from
  524.     binary to ASCII and save it in the .X register;
  525.  
  526.     4.  Fetch the original BCD value from the stack and shift the high
  527.     nybble to the low nybble position;
  528.  
  529.     5.  Convert the low nybble from binary to ASCII.
  530.  
  531. This will result in the .A register containing the tens in ASCII format and
  532. the .X register containing the units (also in ASCII format).  As before,
  533. steps 2 through 5 should be structured into a subroutine so that it may be
  534. called from anywhere in your program.  Then, when you need to decode a BCD
  535. digit you can use the following sequence:
  536.  
  537.              LDA BCD        ;FETCH BCD DIGIT TO DECODE
  538.              JSR BCDAS      ;DECODE
  539.              STA ------     ;SAVE TENS
  540.              STX ------     ;SAVE UNITS
  541.  
  542. The subroutine BCDAS would look like this:
  543.  
  544.     BCDAS    PHA            ;SAVE BCD VALUE ON STACK
  545.                             ;
  546.              AND #$0F       ;MASK HIGH NYBBLE
  547.              CLC
  548.              ADC #$30       ;BINARY TO ASCII
  549.              TAX            ;SAVE UNITS IN .X
  550.                             ;
  551.              PLA            ;FETCH BCD VALUE FROM STACK
  552.                             ;
  553.              LSR A          ;SHIFT HIGH NYBBLE TO...
  554.              LSR A          ;LOW NYBBLE POSITION
  555.              LSR A
  556.              LSR A
  557.                             ;
  558.              CLC
  559.              ADC #$30       ;BINARY TO ASCII
  560.                             ;
  561.              RTS            ;EXIT
  562.  
  563. Upon exit from this routine the .A register will contain the tens, the .X
  564. register will contain the units and the .Y register will be unchanged.  No
  565. RAM is used to execute this routine.  If the high nybble of the BCD value
  566. was zero (as in $07) the .A register will contain an ASCII zero ($30).
  567. Let's see how this routine operates on a BCD digit.  We'll assume that the
  568. BCD value of $57 is to be decoded.
  569.  
  570. After saving the BCD value on the stack the first step is to logically AND
  571. the .A register with $0F, thus masking the high nybble:
  572.  
  573.     .A Register     0101 0111 = $57
  574.     AND #$0F        0000 1111 = $0F
  575.     ===============================
  576.     .A Register     0000 0111 = $07
  577.  
  578. Adding $30 to .A results in a value of $37 which is 7 in ASCII.  This value
  579. is transferred to the .X register and becomes the units value.
  580.  
  581. Next the BCD value is fetched from the stack and subjected to four LSR
  582. operations, each of which shift the high nybble one bit to the right until
  583. it becomes the low nybble.  This results in the following series of values:
  584.  
  585.     .A Register     0101 0111
  586.     1st Shift       0010 1011
  587.     2nd Shift       0001 0101
  588.     3rd Shift       0000 1010
  589.     4th Shift       0000 0101
  590.  
  591. The high nybble is now equal to zero and the low nybble is now equal to
  592. $05.  Adding $30 results in a value of $35 being left in the .A register.
  593. Therefore, upon exit from the BCDAS subroutine .A will contain $35 or ASCII
  594. 5 and .X will contain $37 or 7 in ASCII.  The two digits together result in
  595. ASCII 57.
  596.  
  597. It is apparent that regardless of the BCD value passed to the BCDAS
  598. subroutine two ASCII digits will always result.  In the process of decoding
  599. a BCD number (which would be four bytes long in our examples) it possible
  600. that some leading and/or trailing zeros may result.  A routine to strip
  601. leading and trailing zeros from ASCII numbers will be presented in a later
  602. chapter.
  603.  
  604. VI.  RECAP
  605.  
  606. Up to this point you have learned what a BCD number is, how to properly
  607. organize and store BCD numbers and how to convert ASCII digits to BCD and
  608. vice versa.  Program examples were listed illustrating techniques to make
  609. the necessary conversions from one format to the other.  Before you proceed
  610. beyond this point, I suggest that you code the examples into the computer
  611. using a machine language monitor and run the examples and see what happens.
  612. Don't be afraid of accidentally crashing the machine.  You won't hurt
  613. anything and may learn a lesson or two from it.  Pass various values to the
  614. decoding and encoding routines and see what the routines return in the
  615. registers.  You should terminate your code with a BRK instruction so that
  616. control is returned to the monitor when the code has been executed.
  617.  
  618. Pick anywhere in RAM for the storage needed in ASBCD (which is SFLG2) and
  619. examine SFLG2 upon termination of the routine to see what is left.  Only by
  620. experimenting with these routines will you gain full understanding of how
  621. they work.
  622.  
  623. THE ABC'S OF BCD part 2 will cover techniques for encoding and decoding
  624. multiple byte numbers, and the stripping of leading and trailing zeros.
  625. A thorough understanding of the material just presented is essential to
  626. understanding part 2.  If you are not sure of how a particular machine
  627. language op-code functions consult any good text on 65XX/85XX machine
  628. language programming for a clearer explanation.
  629.  
  630. ------------------------------------------------------------------------
  631. THE ABC'S OF BCD is copyrighted by W.J. Brier and is not to be sold
  632. in any form.  Copies of this article may be distributed by any
  633. medium provided that full credit is given to the author.
  634.